package com.hz.userService.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hz.apiService.model.domain.StudentDTO;
import com.hz.apiService.model.domain.TeacherDTO;
import com.hz.userService.domain.Class;
import com.hz.userService.domain.Grade;
import com.hz.userService.domain.User;
import com.hz.userService.mapper.ClassMapper;
import com.hz.userService.mapper.GradeMapper;
import com.hz.userService.service.UserService;
import com.hz.userService.mapper.UserMapper;
import com.hz.userService.utils.UserHelper;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.regex.Pattern;

/**
 * @author li
 * @description 针对表【users(用户表)】的数据库操作Service实现
 * @createDate 2023-09-23 17:13:32
 */
@Service
@RequiredArgsConstructor  // 使用构造器注入
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {

    private final UserMapper userMapper;
    private final GradeMapper gradeMapper;
    private final ClassMapper classMapper;
    @Resource  // 使用构造器注入方式注入RedisTemplate时失败
    private RedisTemplate<String, Object> redisTemplate;


    @Override
    public List<TeacherDTO> allTeachers() {
        Map<String, Object> map = new HashMap<>();
        map.put("user_type", 2);
        List<User> users = userMapper.selectByMap(map);
        List<TeacherDTO> teachers = new ArrayList<>();
        for (User user : users) {
            TeacherDTO teacherDTO = new TeacherDTO(user.getId(), user.getUsername(),
                    user.getRealName(), user.getStudentId());
            teachers.add(teacherDTO);
        }

        return teachers;
    }

    @Override
    public TeacherDTO getTeacherById(Integer teacherId) {
        User user = userMapper.selectTeacherById(teacherId);
        if (user == null) {
            return null;
        }

        return new TeacherDTO(user.getId(), user.getUsername(),
                user.getRealName(), user.getStudentId());
    }

    @Override
    public StudentDTO getStudentById(Integer studentId) {
        User user = userMapper.selectStudentById(studentId);
        if (user == null){
            return null;
        }
        StudentDTO student = new StudentDTO();
        student.setId(user.getId());
        student.setUsername(user.getUsername());
        student.setRealname(user.getRealName());
        student.setGradeId(user.getGradeId());
        Grade grade = gradeMapper.selectById(user.getGradeId());
        student.setGradeYear(grade.getGradeYear());
        student.setGradeName(grade.getGradeName());
        Class clas = classMapper.selectById((user.getClassId()));
        student.setClassId(clas.getId());
        student.setClassName(clas.getClassName());
        return student;
    }

    @Override
    public int addUser(String username, String realName, Integer sex,
                       String studentId, String password, Integer userType) {
        /* 验证用户名是否已存在 */
        Map<String, Object> map = new HashMap<>();
        map.put("username", username);
        List<User> users = userMapper.selectByMap(map);
        if (users.size() != 0) return 0;

        /* 验证学号是否已存在 */
        map.clear();
        map.put("student_id", studentId);
        users = userMapper.selectByMap(map);
        if (users.size() != 0) return 0;

        /* 设置新增用户基本数据 */
        User user = new User();
        user.setUsername(username);
        user.setRealName(realName);
        user.setSex(sex);
        user.setStudentId(studentId);
        user.setUserType(userType);
        // 新增用户的年级和班级暂不关联
        user.setClassId(null);
        user.setGradeId(null);

        /* 设置该用户密码相关数据 */
        // 1.随机生成加密因子（根据加密因子加密和解密）
        String pwdSalt = UUID.randomUUID().toString().replace("-", "");
        user.setPwdSalt(pwdSalt);
        // 2.将密码明文配合加密因子通过MD5进行加密，并设置新用户加密密码数据
        user.setPwd(UserHelper.createNewPwd(password, pwdSalt));
        // 3.设置密码有效期 7 天
        user.setPwdValidDate(System.currentTimeMillis() + 7 * 24 * 3600 * 1000);

        /* 新增用户并返回结果 */
        return userMapper.insert(user);
    }


    @Override
    @CacheEvict(value = "user_", key = "#id")  // 删除该用户Redis缓存
    public int deleteUserById(Integer id) {
        return userMapper.deleteById(id);
    }


    @Override
    @CachePut(value = "user_", key = "#u.id")  // 将修改结果更新到Redis缓存
    public String updateUser(User u) {
        // 获取指定用户所有信息
        User user = userMapper.selectById(u.getId());

        // 验证修改后用户名是否已存在
        Map<String, Object> map = new HashMap<>();
        map.put("username", u.getUsername());
        List<User> users = userMapper.selectByMap(map);
        if (users.size() != 0) return null;

        // 设置修改后的用户信息
        user.setUsername(u.getUsername());
        user.setSex(u.getSex());
        user.setRealName(u.getRealName());
        user.setClassId(u.getClassId());
        user.setGradeId(u.getGradeId());

        /* 设置该用户密码相关数据 */
        if (!u.getPwd().equals("")) {
            // 1.随机生成加密因子（根据加密因子加密和解密）
            String pwdSalt = UUID.randomUUID().toString().replace("-", "");
            user.setPwdSalt(pwdSalt);

            // 2.将密码明文配合加密因子通过MD5进行加密，并设置新用户加密密码数据
            user.setPwd(UserHelper.createNewPwd(u.getPwd(), pwdSalt));

            // 3.设置密码有效期 7 天
            user.setPwdValidDate(System.currentTimeMillis() + 7 * 24 * 3600 * 1000);
        }

        // 将用户信息修改提交到数据库
        userMapper.updateById(user);

        // 返回修改后用户信息JSON字符串
        return JSON.toJSONString(userMapper.selectById(u.getId()));
    }


    @Override
    @Cacheable(value = "user_", key = "#id")  // 将结果存储到Redis缓存
    public String findUserById(Integer id) {
        return JSON.toJSONString(userMapper.selectById(id));
    }

    @Override
    @Cacheable(value = "user_", key = "#username")  // 将结果存储到Redis缓存
    public String findUserByUsername(String username) {
        Map<String, Object> map = new HashMap<>();
        map.put("username", username);

        // 用户名唯一
        return JSON.toJSONString(userMapper.selectByMap(map).get(0));
    }


    @Override
    // 将登录用户信息缓存到Redis，key为三个参数结合，防止当任意参数不匹配时还是会按照缓存直接登录成功，使用unless防止结果为null时依旧缓存
    // @Cacheable(value = "loginUser_", key = "#username + '&' + '&' + #password + '&' + #userType", unless = "#result == null")
    public String login(String username, String password, Integer userType) {
        /* 根据用户名和用户类型获取用户信息 */
        LambdaQueryWrapper<User> lqWrapper = new LambdaQueryWrapper<>();
        lqWrapper
                .eq(StringUtils.isNotBlank(username), User::getUsername, username)
                .and(i -> i.eq(User::getUserType, userType));
        User user = userMapper.selectOne(lqWrapper);

        // 未获取到用户信息，登录失败
        if (user == null) return null;

        // 登录错误次数超过3次，锁定账户
        if (user.getPwdErrorCount() > 3) return null;

        // 定义MP查询修改条件
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("username", username);

        /* 将用户输入密码配合加密因子进行加密，然后与数据库中存储的加密密码进行匹配，不匹配则登录失败 */
        if (!UserHelper.createNewPwd(password, user.getPwdSalt()).equals(user.getPwd())) {
            // 连续登录失败保护机制，当连续登录次数错误超过 3 次时，锁定账户
            user.setPwdErrorCount(user.getPwdErrorCount() + 1);
            // 提交修改到数据库
            userMapper.update(user, updateWrapper);

            return null;
        }

        // 登录成功，登录错误次数归0
        user.setPwdErrorCount(0);
        // 提交修改到数据库
        userMapper.update(user, updateWrapper);

        // 将登录用户信息转为JSON字符串
        String loginUser = JSON.toJSONString(user);

        // 将登录用户信息更新到Redis缓存
        redisTemplate.opsForValue().set("loginUser_" + username, loginUser);

        return loginUser;
    }


    @Override
    public int logout(String username) {
        // 清除登录用户信息Redis缓存
        redisTemplate.delete("loginUser_" + username);

        return 1;
    }


    @Override
    public int changePasswordById(Integer id, String oldPassword, String newPassword, String newPassword2) {
        /* 获取指定用户信息 */
        User user = userMapper.selectById(id);

        /* 匹配用户输入的旧密码与存储密码是否一致 */
        // 将旧密码按照存储加密因子通过相同规则转换为MD5加密后密码
        String encryptOldPassword = UserHelper.createNewPwd(oldPassword, user.getPwdSalt());
        // 不匹配则返回1
        if (!encryptOldPassword.equals(user.getPwd())) return 1;

        /* 匹配用户两次输入的新密码是否一致，不匹配则返回2 */
        if (!newPassword.equals(newPassword2)) return 2;

        /* 修改密码，返回3 */
        user.setPwd(encryptOldPassword);
        // 重新设置密码有效期 7 天
        user.setPwdValidDate(System.currentTimeMillis() + 7 * 24 * 3600 * 1000);

        // 将修改结果更新到Redis缓存
        redisTemplate.opsForValue().set("user_" + id, user.toString());

        return 3;
    }
}




